using System.Collections.Generic;

//
// * Copyright (c) 2002-2009 "Neo Technology,"
// *     Network Engine for Objects in Lund AB [http://neotechnology.com]
// *
// * This file is part of Neo4j.
// * 
// * Neo4j is free software: you can redistribute it and/or modify
// * it under the terms of the GNU Affero General Public License as
// * published by the Free Software Foundation, either version 3 of the
// * License, or (at your option) any later version.
// * 
// * This program is distributed in the hope that it will be useful,
// * but WITHOUT ANY WARRANTY; without even the implied warranty of
// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// * GNU Affero General Public License for more details.
// * 
// * You should have received a copy of the GNU Affero General Public License
// * along with this program. If not, see <http://www.gnu.org/licenses/>.
// 
namespace org.neo4j.kernel.impl.core
{


	using Direction = org.neo4j.graphdb.Direction;
	using NotFoundException = org.neo4j.graphdb.NotFoundException;
	using Relationship = org.neo4j.graphdb.Relationship;
	using RelationshipType = org.neo4j.graphdb.RelationshipType;

	internal class IntArrayIterator : IEnumerable <Relationship>, IEnumerator <Relationship>
	{
		private Logger log = Logger.getLogger(typeof(IntArrayIterator).Name);

		private IEnumerator <RelTypeElementIterator> typeIterator;
		private RelTypeElementIterator currentTypeIterator = null;
		private NodeImpl fromNode;
		private Direction direction = null;
		private Relationship nextElement = null;
		private readonly NodeManager nodeManager;
		private readonly RelationshipType[] types;

		private Set<string> visitedTypes = new HashSet<string>();

		internal IntArrayIterator(List<RelTypeElementIterator> rels, NodeImpl fromNode, Direction direction, NodeManager nodeManager, RelationshipType[] types)
		{
			this.typeIterator = rels.GetEnumerator();
			if (typeIterator.MoveNext())
			{
				currentTypeIterator = typeIterator.Current;
				visitedTypes.Add(currentTypeIterator.getType());
			}
			else
			{
				currentTypeIterator = new NullRelTypeElement();
			}
			this.fromNode = fromNode;
			this.direction = direction;
			this.nodeManager = nodeManager;
			this.types = types;
		}

//    IntArrayIterator( IEnumerator <RelTypeElementIterator> rels, NodeImpl fromNode,
//        Direction direction, NodeManager nodeManager )
//    {
//        this.typeIterator = rels;
//        this.fromNode = fromNode;
//        this.direction = direction;
//        this.nodeManager = nodeManager;
//    }

		public virtual IEnumerator <Relationship> iterator()
		{
			return this;
		}

		public virtual bool hasNext()
		{
			if (nextElement != null)
			{
				return true;
			}
			do
			{
				if (currentTypeIterator.MoveNext())
				{
					int nextId = currentTypeIterator.Current;
					try
					{
						Relationship possibleElement = nodeManager.getRelationshipById(nextId);
						if (direction == Direction.INCOMING && possibleElement.getEndNode().Equals(fromNode))
						{
							nextElement = possibleElement;
							return true;
						}
						else if (direction == Direction.OUTGOING && possibleElement.getStartNode().Equals(fromNode))
						{
							nextElement = possibleElement;
							return true;
						}
						else if (direction == Direction.BOTH)
						{
							nextElement = possibleElement;
							return true;
						}
					}
					catch (NotFoundException e)
					{
						log.log(Level.FINE, "Unable to get relationship " + nextId, e);
					}
				}
				while (!currentTypeIterator.MoveNext())
				{
					if (typeIterator.MoveNext())
					{
						currentTypeIterator = typeIterator.Current;
						visitedTypes.Add(currentTypeIterator.getType());
					}
					else
					{
						bool gotMore = fromNode.getMoreRelationships();
						List<RelTypeElementIterator> list = Collections.EMPTY_LIST;
						if (types.Length == 0)
						{
							list = fromNode.getAllRelationships();
						}
						else
						{
							list = fromNode.getAllRelationshipsOfType(types);
						}
						List<RelTypeElementIterator>.Enumerator itr = list.GetEnumerator();
						while (itr.MoveNext())
						{
							RelTypeElementIterator element = itr.Current;
							if (visitedTypes.Contains(element.getType()))
							{
								itr.Remove();
							}
						}
						typeIterator = list.GetEnumerator();
						if (typeIterator.MoveNext())
						{
							currentTypeIterator = typeIterator.Current;
							visitedTypes.Add(currentTypeIterator.getType());
						}
						if (!gotMore)
						{
							break;
						}
					}
				}
			 } while (currentTypeIterator.MoveNext());
		// no next element found
			return false;
		}

		public virtual Relationship next()
		{
			hasNext();
			if (nextElement != null)
			{
				Relationship elementToReturn = nextElement;
				nextElement = null;
				return elementToReturn;
			}
			throw new NoSuchElementException();
		}

		public virtual void remove()
		{
			throw new UnsupportedOperationException();
		}
	}

}